﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.VisualBasic.PowerPacks;

namespace BattleshipGameClient
{
    public partial class GameForm : Form
    {
        //Image cellTexture;
        private Board playerBoard;
        private Board opponentBoard;

        // For the placement
        private Ship.Type selectedShip = Ship.Type.None;
        private Ship.Position position = Ship.Position.Horizontal;
        private List<Board.Cell> tempSelectedCells;
        private RectangleShape selectedGraphCell;
        private Fleet fleet;
        private Connection conn;

        public GameForm(Connection conn)
        {
            InitializeComponent();
            this.conn = conn;
            this.Closing += new CancelEventHandler(Form_Closing);

            // Create the Player Board
            Point offset = new Point(10, 10);

            this.playerBoard = new Board(10, 10, 40, offset);
            this.playerBoard.setGraphGridParent(this);
            this.playerBoard.getGraphGrid().BringToFront();
            this.tempSelectedCells = new List<Board.Cell>();
            this.fleet = conn.myFleet;

            // Name Label
            this.leftNameLabel.Text = conn.getName() + " (You)";
            placingStatus.Text = "";

            // Applying texture to the Cell. BUG: Lagging
            //cellTexture = Image.FromFile("sea.jpg");
            //board.setGraphCellTexture(cellTexture);

            foreach (KeyValuePair<string, Board.Cell> pair in playerBoard.getCellDictionary())
            {
                pair.Value.GraphCell.MouseEnter += new EventHandler(placementGraphCell_MouseEnter);
                pair.Value.GraphCell.MouseLeave += new EventHandler(placementGraphCell_MouseLeave);
                pair.Value.GraphCell.KeyDown += new KeyEventHandler(placementShipShape_KeyDown);
                pair.Value.GraphCell.Click += new EventHandler(placementGraphCell_Click);
            }
        }
        private void Form_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            conn.endGame();
        }

        private void placementGraphCell_MouseEnter(object sender, EventArgs e)
        {
            selectedGraphCell = (RectangleShape)sender;
            int[] coordCell = { Convert.ToInt32(selectedGraphCell.Name.Split(';')[0]), Convert.ToInt32(selectedGraphCell.Name.Split(';')[1]) };
            List<int[]> coordShip = new List<int[]>();
            this.tempSelectedCells.Clear();
            if (selectedShip == Ship.Type.Carrier)
            {
                if (position == Ship.Position.Vertical)
                {
                    int[] coord1 = { coordCell[0], (coordCell[1] - 2) };
                    int[] coord2 = { coordCell[0], (coordCell[1] - 1) };
                    int[] coord3 = coordCell;
                    int[] coord4 = { coordCell[0], (coordCell[1] + 1) };
                    int[] coord5 = { coordCell[0], (coordCell[1] + 2) };
                    coordShip.Add(coord1);
                    coordShip.Add(coord2);
                    coordShip.Add(coord3);
                    coordShip.Add(coord4);
                    coordShip.Add(coord5);
                }
                else
                {
                    int[] coord1 = { (coordCell[0] - 2), coordCell[1] };
                    int[] coord2 = { (coordCell[0] - 1), coordCell[1] };
                    int[] coord3 = coordCell;
                    int[] coord4 = { (coordCell[0] + 1), coordCell[1] };
                    int[] coord5 = { (coordCell[0] + 2), coordCell[1] };
                    coordShip.Add(coord1);
                    coordShip.Add(coord2);
                    coordShip.Add(coord3);
                    coordShip.Add(coord4);
                    coordShip.Add(coord5);
                }
            }
            else if (selectedShip == Ship.Type.Battleship)
            {
                if (position == Ship.Position.Vertical)
                {
                    int[] coord1 = { coordCell[0], (coordCell[1] - 2) };
                    int[] coord2 = { coordCell[0], (coordCell[1] - 1) };
                    int[] coord3 = coordCell;
                    int[] coord4 = { coordCell[0], (coordCell[1] + 1) };
                    coordShip.Add(coord1);
                    coordShip.Add(coord2);
                    coordShip.Add(coord3);
                    coordShip.Add(coord4);
                }
                else
                {
                    int[] coord1 = { (coordCell[0] - 2), coordCell[1] };
                    int[] coord2 = { (coordCell[0] - 1), coordCell[1] };
                    int[] coord3 = coordCell;
                    int[] coord4 = { (coordCell[0] + 1), coordCell[1] };
                    coordShip.Add(coord1);
                    coordShip.Add(coord2);
                    coordShip.Add(coord3);
                    coordShip.Add(coord4);
                }
            }
            else if (selectedShip == Ship.Type.Destroyer)
            {
                if (position == Ship.Position.Vertical)
                {
                    int[] coord1 = { coordCell[0], (coordCell[1] - 1) };
                    int[] coord2 = coordCell;
                    int[] coord3 = { coordCell[0], (coordCell[1] + 1) };
                    coordShip.Add(coord1);
                    coordShip.Add(coord2);
                    coordShip.Add(coord3);
                }
                else
                {
                    int[] coord1 = { (coordCell[0] - 1), coordCell[1] };
                    int[] coord2 = coordCell;
                    int[] coord3 = { (coordCell[0] + 1), coordCell[1] };
                    coordShip.Add(coord1);
                    coordShip.Add(coord2);
                    coordShip.Add(coord3);
                }
            }
            else if (selectedShip == Ship.Type.Submarine)
            {
                if (position == Ship.Position.Vertical)
                {
                    int[] coord1 = { coordCell[0], (coordCell[1] - 1) };
                    int[] coord2 = coordCell;
                    int[] coord3 = { coordCell[0], (coordCell[1] + 1) };
                    coordShip.Add(coord1);
                    coordShip.Add(coord2);
                    coordShip.Add(coord3);
                }
                else
                {
                    int[] coord1 = { (coordCell[0] - 1), coordCell[1] };
                    int[] coord2 = coordCell;
                    int[] coord3 = { (coordCell[0] + 1), coordCell[1] };
                    coordShip.Add(coord1);
                    coordShip.Add(coord2);
                    coordShip.Add(coord3);
                }
            }
            else if (selectedShip == Ship.Type.PatrolBoat)
            {
                if (position == Ship.Position.Vertical)
                {
                    int[] coord1 = { coordCell[0], (coordCell[1] - 1) };
                    int[] coord2 = coordCell;
                    coordShip.Add(coord1);
                    coordShip.Add(coord2);
                }
                else
                {
                    int[] coord1 = { (coordCell[0] - 1), coordCell[1] };
                    int[] coord2 = coordCell;
                    coordShip.Add(coord1);
                    coordShip.Add(coord2);
                }
            }
            foreach (int[] coord in coordShip)
            {
                if ((1 <= coord[0]) && (coord[0] <= playerBoard.GridWidth) && (1 <= coord[1]) && (coord[1] <= playerBoard.GridHeight))
                {
                    string coordKey = coord[0].ToString() + ";" + coord[1].ToString();
                    Board.Cell selectionCell = playerBoard.getCellDictionary()[coordKey];
                    if (selectionCell.CurrentCell == Board.Cell.cellState.WATER)
                    {
                        selectionCell.GraphCell.BackStyle = BackStyle.Opaque;
                        selectionCell.GraphCell.BackColor = Color.Red;
                        this.tempSelectedCells.Add(selectionCell);
                    }
                    else
                    {
                        foreach (Board.Cell cell in tempSelectedCells)
                        {
                            cell.GraphCell.BackStyle = BackStyle.Transparent;
                        }
                        this.tempSelectedCells.Clear();
                        break;
                    }
                }
                else
                {
                    foreach (Board.Cell cell in tempSelectedCells)
                    {
                        cell.GraphCell.BackStyle = BackStyle.Transparent;
                    }
                    this.tempSelectedCells.Clear();
                    break;
                }

            }
        }

        private void placementGraphCell_MouseLeave(object sender, EventArgs e)
        {
            foreach (Board.Cell cell in tempSelectedCells)
            {
                if (cell.CurrentCell == Board.Cell.cellState.WATER)
                {
                    cell.GraphCell.BackStyle = BackStyle.Transparent;
                    cell.GraphCell.Refresh();
                }
            }
            this.placementRefresh();
        }

        private void placementShipShape_KeyDown(object sender, KeyEventArgs e)
        {
            if (this.position == Ship.Position.Vertical)
            { this.position = Ship.Position.Horizontal; }
            else { this.position = Ship.Position.Vertical; }
            foreach (Board.Cell cell in this.tempSelectedCells)
            {
                if (cell.CurrentCell == Board.Cell.cellState.WATER)
                {
                    cell.GraphCell.BackStyle = BackStyle.Transparent;
                    cell.GraphCell.Refresh();
                }
            }
            this.tempSelectedCells.Clear();
            this.placementRefresh();
            this.placementGraphCell_MouseEnter(this.selectedGraphCell, e);
        }

        private void placementRefresh()
        {
            foreach (KeyValuePair<Ship.Type, Ship> fleetPair in this.fleet.Ships)
            {
                foreach (Board.Cell cell in fleetPair.Value.GraphShip)
                {
                    cell.GraphCell.Refresh();
                }
            }
            this.grid1Background.Refresh();
        }

        private void placemntShipShape_Click(object sender, EventArgs e)
        {
            RectangleShape boatShape = (RectangleShape)sender;
            this.submarine.BackColor = Color.SeaGreen;
            this.patrolboat.BackColor = Color.SeaGreen;
            this.carrier.BackColor = Color.SeaGreen;
            this.battleship.BackColor = Color.SeaGreen;
            this.destroyer.BackColor = Color.SeaGreen;
            boatShape.BackColor = Color.Red;
            switch (boatShape.Name)
            {
                case "battleship":
                    this.selectedShip = Ship.Type.Battleship;
                    break;
                case "carrier":
                    this.selectedShip = Ship.Type.Carrier;
                    break;
                case "destroyer":
                    this.selectedShip = Ship.Type.Destroyer;
                    break;
                case "patrolboat":
                    this.selectedShip = Ship.Type.PatrolBoat;
                    break;
                case "submarine":
                    this.selectedShip = Ship.Type.Submarine;
                    break;
                default:
                    break;
            }
        }

        private void placementGraphCell_Click(object sender, EventArgs e)
        {
            if (this.tempSelectedCells.Count > 1)
            {
                if (fleet.Ships.ContainsKey(selectedShip))
                {
                    foreach (Board.Cell cell in fleet.Ships[selectedShip].GraphShip)
                    {
                        cell.GraphCell.BackStyle = BackStyle.Transparent;
                        cell.CurrentCell = Board.Cell.cellState.WATER;
                        cell.GraphCell.Refresh();
                    }
                    fleet.Ships[selectedShip].GraphShip.Clear();
                    this.grid1Background.Refresh();
                    fleet.Ships[selectedShip].GraphShip = new List<Board.Cell>(this.tempSelectedCells);
                }
                else
                {
                    fleet.Ships.Add(selectedShip, new Ship(selectedShip, new List<Board.Cell>(this.tempSelectedCells)));
                }


                foreach (Board.Cell cell in fleet.Ships[selectedShip].GraphShip)
                {
                    cell.GraphCell.BackStyle = BackStyle.Opaque;
                    cell.GraphCell.BackColor = Color.Blue;
                    cell.CurrentCell = Board.Cell.cellState.SHIPUNIT;
                }
                if (fleet.Ships.Count == 5)
                {
                    this.validateButton.Enabled = true;
                }
            }
        }

        private void placementValidateButton_Click(object sender, EventArgs e)
        {
            boatGroupBox.Enabled = false;
            conn.selfReadyToStart();

            placingStatus.Text = "Waiting for " + conn.getRemoteName() + " to place his ships.";
        }

        public void startShooting()
        {
            this.Invoke((MethodInvoker)delegate
            {
                endPlacement();
            });
            
        }

        private void endPlacement()
        {
            placingStatus.Hide();
            if (conn.getState() == Connection.States.isTarget)
                shootButton.Enabled = false;

            Point offset = new Point(400, 0);

            foreach (KeyValuePair<string, Board.Cell> pair in playerBoard.getCellDictionary())
            {
                pair.Value.GraphCell.MouseEnter -= placementGraphCell_MouseEnter;
                pair.Value.GraphCell.MouseLeave -= placementGraphCell_MouseLeave;
                pair.Value.GraphCell.KeyDown -= placementShipShape_KeyDown;
                pair.Value.GraphCell.Click -= placementGraphCell_Click;
                pair.Value.CurrentCell = Board.Cell.cellState.WATER;
                pair.Value.GraphCell.Location = new Point(pair.Value.GraphCell.Location.X + 430, pair.Value.GraphCell.Location.Y);
            }

            this.grid2Background.Visible = true;
            selectedShip = Ship.Type.None;
            this.GameBoards();
            this.boatGroupBox.Dispose();
        }

        private void GameBoards()
        {
            // Create the Opponent Board
            Point offset = new Point(10, 10);

            this.opponentBoard = new Board(10, 10, 40, offset);
            this.opponentBoard.setGraphGridParent(this);
            this.playerBoard.getGraphGrid().BringToFront();
            this.opponentBoard.getGraphGrid().BringToFront();

            // Shoot Button
            this.shootButton.Visible = true;
            this.shootButton.Enabled = false;

            // Name label
            this.rightNameLabel.Text = this.leftNameLabel.Text;
            this.leftNameLabel.Text = conn.getRemoteName() + " (Opponent)";
            this.rightNameLabel.Visible = true;


            // Applying texture to the Cell. BUG: Lagging
            //cellTexture = Image.FromFile("sea.jpg");
            //opponentBoard.setGraphCellTexture(cellTexture);

            foreach (KeyValuePair<string, Board.Cell> pair in opponentBoard.getCellDictionary())
            {
                pair.Value.GraphCell.MouseEnter += new EventHandler(gameGraphCell_MouseEnter);
                pair.Value.GraphCell.Click += new EventHandler(gameGraphCell_Click);
            }
        }

        private void gameGraphCell_MouseEnter(object sender, EventArgs e)
        {
            RectangleShape hoverGraphCell = (RectangleShape)sender;

            // HELP: Coordinate of the cell : ( x:[1->10], y:[1->10] )
            int[] coordCell = { Convert.ToInt32(hoverGraphCell.Name.Split(';')[0]), Convert.ToInt32(hoverGraphCell.Name.Split(';')[1]) };

            // HELP: Cell corresponding of the selected graphical Cell
            Board.Cell selectionCell = opponentBoard.getCellDictionary()[hoverGraphCell.Name];

            // Test
            this.eventText.Text = hoverGraphCell.Name + " " + opponentBoard.getCellDictionary()[hoverGraphCell.Name].CurrentCell.ToString();
        }

        private void gameGraphCell_Click(object sender, EventArgs e)
        {
            Board.Cell lastSelectionCell = opponentBoard.getCellDictionary()[selectedGraphCell.Name];
            if (lastSelectionCell.CurrentCell == Board.Cell.cellState.SCAN)
            {
                lastSelectionCell.CurrentCell = Board.Cell.cellState.WATER;
                lastSelectionCell.GraphCell.BackStyle = BackStyle.Transparent;
                lastSelectionCell.GraphCell.Refresh();
            }
            
            this.selectedGraphCell = (RectangleShape)sender;
            Board.Cell selectionCell = opponentBoard.getCellDictionary()[selectedGraphCell.Name];

            if (selectionCell.CurrentCell == Board.Cell.cellState.WATER)
            {
                selectionCell.CurrentCell = Board.Cell.cellState.SCAN;
                selectionCell.GraphCell.BackStyle = BackStyle.Opaque;
                selectionCell.GraphCell.BackColor = Color.Gray;
                selectionCell.GraphCell.Refresh();
                if (conn.getState() == Connection.States.aiming)
                {
                    this.shootButton.Enabled = true;
                }
            }
            else
            {
                this.shootButton.Enabled = false;
            }

            this.gameRefresh();

            // Test
            this.gameUpdateTestLabel();
        }

        private void gameShootButton_Click(object sender, EventArgs e)
        {
            Board.Cell selectionCell = opponentBoard.getCellDictionary()[this.selectedGraphCell.Name];

            if (conn.getState() == Connection.States.aiming && selectionCell.CurrentCell == Board.Cell.cellState.SCAN)
            {
                string[] coords = selectedGraphCell.Name.Split(';');
                Point coordinates = new Point(Int32.Parse(coords[0]),Int32.Parse(coords[1]));
                conn.selfShoot(coordinates);

                // TODO: Use the real point value here.
                selectionCell.CurrentCell = Board.Cell.cellState.HIT;
                selectionCell.GraphCell.BackStyle = BackStyle.Opaque;
                selectionCell.GraphCell.BackColor = Color.Black;
                selectionCell.GraphCell.Refresh();

                // newStatus should be received from the opponent after sending the coordinates to him.
                Board.Cell.cellState newStatus = Board.Cell.cellState.MISS;

                this.gameUpdateOpponentCell(coordinates, newStatus);

            }

            this.gameRefresh();
        }

        private void gameUpdateTestLabel()
        {
            this.eventText.Text = selectedGraphCell.Name + " " + opponentBoard.getCellDictionary()[selectedGraphCell.Name].CurrentCell.ToString();
        }

        private void gameRefresh()
        {
            foreach(KeyValuePair<string, Board.Cell> pair in opponentBoard.getCellDictionary())
            {
                if (pair.Value.CurrentCell != Board.Cell.cellState.WATER)
                {
                    pair.Value.GraphCell.Refresh();
                }
            }
            this.grid1Background.Refresh();
        }


        internal void setOpponentReadyToStart()
        {
            if (InvokeRequired)
            {
                this.Invoke((MethodInvoker)delegate
                {
                    setOpponentReadyToStart();
                });
            } else
                placingStatus.Text = "Your opponent " + conn.getRemoteName() + " has placed his ships and are waiting for you.";

        }

        private void gameUpdatePlayerCell(Point coordinate, Board.Cell.cellState newStatus)
        {
            string coordinates = coordinate.X.ToString() + ";" + coordinate.Y.ToString();
            Board.Cell selectionCell = playerBoard.getCellDictionary()[coordinates];

            if (newStatus == Board.Cell.cellState.MISS)
            {
                selectionCell.CurrentCell = Board.Cell.cellState.MISS;
                selectionCell.GraphCell.BackStyle = BackStyle.Opaque;
                selectionCell.GraphCell.BackColor = Color.DarkBlue;
                selectionCell.GraphCell.Refresh();
            }
            if (newStatus == Board.Cell.cellState.HIT)
            {
                selectionCell.CurrentCell = Board.Cell.cellState.HIT;
                selectionCell.GraphCell.BackStyle = BackStyle.Opaque;
                selectionCell.GraphCell.BackColor = Color.Orange;
                selectionCell.GraphCell.Refresh();
            }
            if (newStatus == Board.Cell.cellState.SINK)
            {
                selectionCell.CurrentCell = Board.Cell.cellState.HIT;
                selectionCell.GraphCell.BackStyle = BackStyle.Opaque;
                selectionCell.GraphCell.BackColor = Color.Red;
                selectionCell.GraphCell.Refresh();
            }

            foreach (Board.Cell cell in playerBoard.getCellDictionary().Values)
            {
                cell.GraphCell.Refresh();
            }

            this.grid2Background.Refresh();
        }

        internal void gameUpdateOpponentCell(Point coordinate, Board.Cell.cellState newStatus)
        {
            string coordinates = coordinate.X.ToString() + ";" + coordinate.Y.ToString();
            Board.Cell selectionCell = opponentBoard.getCellDictionary()[coordinates];

            if (newStatus == Board.Cell.cellState.MISS)
            {
                selectionCell.CurrentCell = Board.Cell.cellState.MISS;
                selectionCell.GraphCell.BackStyle = BackStyle.Opaque;
                selectionCell.GraphCell.BackColor = Color.DarkBlue;
                selectionCell.GraphCell.Refresh();
            }
            if (newStatus == Board.Cell.cellState.HIT)
            {
                selectionCell.CurrentCell = Board.Cell.cellState.HIT;
                selectionCell.GraphCell.BackStyle = BackStyle.Opaque;
                selectionCell.GraphCell.BackColor = Color.Orange;
                selectionCell.GraphCell.Refresh();
            }
            if (newStatus == Board.Cell.cellState.SINK)
            {
                selectionCell.CurrentCell = Board.Cell.cellState.HIT;
                selectionCell.GraphCell.BackStyle = BackStyle.Opaque;
                selectionCell.GraphCell.BackColor = Color.Red;
                selectionCell.GraphCell.Refresh();
            }

            foreach (Board.Cell cell in opponentBoard.getCellDictionary().Values)
            {
                cell.GraphCell.Refresh();
            }

            this.grid1Background.Refresh();
        }


        internal void hitDisplay(hitReport shot, bool self = false)
        {
            this.Invoke((MethodInvoker)delegate
            {
                string who;
                if (self)
                {
                    who = "Opponent";
                }

                else
                {
                    who = "You";
                }
            
                if (shot.getShipType() == Ship.Type.None)
                {
                    hitText.Text = who + " missed";
                    if (self)
                        gameUpdatePlayerCell(shot.getCoord(), Board.Cell.cellState.MISS);
                    else
                        gameUpdateOpponentCell(shot.getCoord(), Board.Cell.cellState.MISS);
                }
                else
                {
                    if (self)
                        gameUpdatePlayerCell(shot.getCoord(), Board.Cell.cellState.HIT);
                    else
                        gameUpdateOpponentCell(shot.getCoord(), Board.Cell.cellState.HIT);

                    if (shot.IsSunk())
                    {
                        hitText.Text = who + " sunk the " + shot.getShipType();
                    }

                    else
                    {
                        hitText.Text = who + " hit the " + shot.getShipType();
                    }
                    gameUpdateOpponentCell(shot.getCoord(), Board.Cell.cellState.HIT);
                }

                hitText.Show();
                shootButton.Enabled = self;
                // TODO: Update cell
            });
        }

    }
}
